using System;
using System.Threading;
using System.Windows.Forms;
using CSW.Framework.Common;
using CSW.Framework.Common.Exceptions;
using CSW.Framework.Common.Utils;
using DevExpress.XtraEditors;

namespace CSW.Framework.UI.DevEx.Forms
{
    /// <summary>
    /// Progress form.  See <see cref="AsyncProcessDelegate{ParameterType,ResultType}" />.
    /// </summary>
    public partial class DXProgressForm<T1, T2, T3, T4, TResult> : XtraForm
    {
        private AsyncProcessDelegate<T1, T2, T3, T4, TResult> m_AsyncProcessDelegate;
        private T1 m_Arg1;
        private T2 m_Arg2;
        private T3 m_Arg3;
        private T4 m_Arg4;
        private TResult m_Result;
        private bool m_AsyncFinished;
        private DateTime m_StartTime;
        private bool m_Cancel;
        private ExceptionHandlerDelegate m_ExceptionHandler;

        private static bool m_AllowCancel;

        private DXProgressForm()
        {
            InitializeComponent();

            string gifPath = PathUtils.GetTempFileName(".gif");
            Properties.Resources.indicator_flower.Save(gifPath);
            webBrowser1.Url = new Uri("file://" + gifPath);
        }

        /// <summary>
        /// Runs the specified asynchronous process delegate.
        /// </summary>
        /// <param name="asyncProcessDelegate">The async process delegate.</param>
        /// <param name="arg1">The first argument.</param>
        /// <param name="arg2">The second argument.</param>
        /// <param name="arg3">The third argument.</param>
        /// <param name="arg4">The fourth argument.</param>
        /// <returns>The result.</returns>
        public static TResult RunWithCancel(AsyncProcessDelegate<T1, T2, T3, T4, TResult> asyncProcessDelegate, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            m_AllowCancel = true;
            TResult result = Run(asyncProcessDelegate, arg1, arg2, arg3, arg4, ExceptionHelper.ShowException, null);
            m_AllowCancel = false;
            return result;
        }

        /// <summary>
        /// Runs the specified asynchronous process delegate.
        /// </summary>
        /// <param name="asyncProcessDelegate">The async process delegate.</param>
        /// <param name="arg1">The first argument.</param>
        /// <param name="arg2">The second argument.</param>
        /// <param name="arg3">The third argument.</param>
        /// <param name="arg4">The fourth argument.</param>
        /// <param name="owner">Shows the progress form with the specified owner.</param>
        /// <returns>The result.</returns>
        public static TResult RunWithCancel(AsyncProcessDelegate<T1, T2, T3, T4, TResult> asyncProcessDelegate, T1 arg1, T2 arg2, T3 arg3, T4 arg4, IWin32Window owner)
        {
            m_AllowCancel = true;
            TResult result = Run(asyncProcessDelegate, arg1, arg2, arg3, arg4, ExceptionHelper.ShowException, owner);
            m_AllowCancel = false;
            return result;
        }

        /// <summary>
        /// Runs the specified asynchronous process delegate.
        /// </summary>
        /// <param name="asyncProcessDelegate">The async process delegate.</param>
        /// <param name="arg1">The first argument.</param>
        /// <param name="arg2">The second argument.</param>
        /// <param name="arg3">The third argument.</param>
        /// <param name="arg4">The fourth argument.</param>
        /// <param name="exceptionHandler">The exception handler.</param>
        /// <returns>The result.</returns>
        public static TResult RunWithCancel(AsyncProcessDelegate<T1, T2, T3, T4, TResult> asyncProcessDelegate, T1 arg1, T2 arg2, T3 arg3, T4 arg4, ExceptionHandlerDelegate exceptionHandler)
        {
            m_AllowCancel = true;
            TResult result = Run(asyncProcessDelegate, arg1, arg2, arg3, arg4, exceptionHandler, null);
            m_AllowCancel = false;
            return result;
        }

        /// <summary>
        /// Runs the specified asynchronous process delegate.
        /// </summary>
        /// <param name="asyncProcessDelegate">The async process delegate.</param>
        /// <param name="arg1">The first argument.</param>
        /// <param name="arg2">The second argument.</param>
        /// <param name="arg3">The third argument.</param>
        /// <param name="arg4">The fourth argument.</param>
        /// <param name="exceptionHandler">The exception handler.</param>
        /// <param name="owner">Shows the progress form with the specified owner.</param>
        /// <returns>The result.</returns>
        public static TResult RunWithCancel(AsyncProcessDelegate<T1, T2, T3, T4, TResult> asyncProcessDelegate, T1 arg1, T2 arg2, T3 arg3, T4 arg4, ExceptionHandlerDelegate exceptionHandler, IWin32Window owner)
        {
            m_AllowCancel = true;
            TResult result = Run(asyncProcessDelegate, arg1, arg2, arg3, arg4, exceptionHandler, owner);
            m_AllowCancel = false;
            return result;
        }

        /// <summary>
        /// Runs the specified asynchronous process delegate.
        /// </summary>
        /// <param name="asyncProcessDelegate">The async process delegate.</param>
        /// <param name="arg1">The first argument.</param>
        /// <param name="arg2">The second argument.</param>
        /// <param name="arg3">The third argument.</param>
        /// <param name="arg4">The fourth argument.</param>
        /// <returns>The result.</returns>
        public static TResult Run(AsyncProcessDelegate<T1, T2, T3, T4, TResult> asyncProcessDelegate, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            return Run(asyncProcessDelegate, arg1, arg2, arg3, arg4, ExceptionHelper.ShowException, null);
        }

        /// <summary>
        /// Runs the specified asynchronous process delegate.
        /// </summary>
        /// <param name="asyncProcessDelegate">The async process delegate.</param>
        /// <param name="arg1">The first argument.</param>
        /// <param name="arg2">The second argument.</param>
        /// <param name="arg3">The third argument.</param>
        /// <param name="arg4">The fourth argument.</param>
        /// <param name="owner">Shows the progress form with the specified owner.</param>
        /// <returns>The result.</returns>
        public static TResult Run(AsyncProcessDelegate<T1, T2, T3, T4, TResult> asyncProcessDelegate, T1 arg1, T2 arg2, T3 arg3, T4 arg4, IWin32Window owner)
        {
            return Run(asyncProcessDelegate, arg1, arg2, arg3, arg4, ExceptionHelper.ShowException, owner);
        }

        /// <summary>
        /// Runs the specified asynchronous process delegate.
        /// </summary>
        /// <param name="asyncProcessDelegate">The async process delegate.</param>
        /// <param name="arg1">The first argument.</param>
        /// <param name="arg2">The second argument.</param>
        /// <param name="arg3">The third argument.</param>
        /// <param name="arg4">The fourth argument.</param>
        /// <param name="exceptionHandler">The exception handler.</param>
        /// <returns>The result.</returns>
        public static TResult Run(AsyncProcessDelegate<T1, T2, T3, T4, TResult> asyncProcessDelegate, T1 arg1, T2 arg2, T3 arg3, T4 arg4, ExceptionHandlerDelegate exceptionHandler)
        {
            return Run(asyncProcessDelegate, arg1, arg2, arg3, arg4, exceptionHandler, null);
        }

        /// <summary>
        /// Runs the specified asynchronous process delegate.
        /// </summary>
        /// <param name="asyncProcessDelegate">The async process delegate.</param>
        /// <param name="arg1">The first argument.</param>
        /// <param name="arg2">The second argument.</param>
        /// <param name="arg3">The third argument.</param>
        /// <param name="arg4">The fourth argument.</param>
        /// <param name="exceptionHandler">The exception handler.</param>
        /// <param name="owner">Shows the progress form with the specified owner.</param>
        /// <returns>The result.</returns>
        public static TResult Run(AsyncProcessDelegate<T1, T2, T3, T4, TResult> asyncProcessDelegate, T1 arg1, T2 arg2, T3 arg3, T4 arg4, ExceptionHandlerDelegate exceptionHandler, IWin32Window owner)
        {
            Guard.ArgumentNotNull(asyncProcessDelegate, "asyncProcessDelegate");
            Guard.ArgumentNotNull(exceptionHandler, "exceptionHandler");

            using (DXProgressForm<T1, T2, T3, T4, TResult> progressForm = new DXProgressForm<T1, T2, T3, T4, TResult>())
            {
                progressForm.btnCancel.Visible = m_AllowCancel;

                progressForm.m_AsyncProcessDelegate = asyncProcessDelegate;
                progressForm.m_Arg1 = arg1;
                progressForm.m_Arg2 = arg2;
                progressForm.m_Arg3 = arg3;
                progressForm.m_Arg4 = arg4;
                progressForm.m_ExceptionHandler = exceptionHandler;

                progressForm.Text = "0m 00s";
                progressForm.m_StartTime = DateTime.Now;
                progressForm.timer1.Enabled = true;

                Thread t = new Thread(progressForm.RunAsyncProcess);
                t.Start();

                if (owner == null)
                    progressForm.ShowDialog();
                else
                    progressForm.ShowDialog(owner);

                return progressForm.m_Result;
            }
        }

        private bool ShouldCancel()
        {
            return m_Cancel;
        }

        private void UIThreadInvoker(FuncVoid method)
        {
            if (!InvokeRequired)
                method();
            else
                Invoke(method);
        }

        private void RunAsyncProcess()
        {
            try
            {
                AsyncProcessHelper helper = new AsyncProcessHelper(UpdateStatus, ShouldCancel, UIThreadInvoker);

                m_Result = m_AsyncProcessDelegate(helper, m_Arg1, m_Arg2, m_Arg3, m_Arg4);
            }
            catch (Exception ex)
            {
                m_ExceptionHandler(ex);
            }
            finally
            {
                m_AsyncFinished = true;
                CloseForm();
            }
        }

        private void CloseForm()
        {
            if (!InvokeRequired)
                Close();
            else
                Invoke(new MethodInvoker(CloseForm));
        }

        private void UpdateStatus(string caption, string message, int? percentComplete)
        {
            if (!InvokeRequired)
                lblProgress.Text = message;
            else
                Invoke(new UpdateStatusDelegate(UpdateStatus), caption, message, percentComplete);
        }

        private void ProgressForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing && !m_AsyncFinished)
                e.Cancel = true;
            else
                timer1.Enabled = false;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            TimeSpan elapsed = DateTime.Now - m_StartTime;
            Text = string.Format("{0:0}m {1:00}s", Math.Floor(elapsed.TotalMinutes), elapsed.Seconds);
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            m_Cancel = true;
            btnCancel.Enabled = false;
        }
    }
}